package loger

import (
	"bytes"
	"fmt"
	"os"
	"sync"
	"time"
)

var buffers *sync.Pool

func init() {
	buffers = &sync.Pool{
		New: func() interface{} {
			return new(bytes.Buffer)
		},
	}
}

type Entry struct {
	logger *Logger
	buffer *bytes.Buffer

	Data    map[string]interface{}
	Level   Level
	Time    time.Time
	Message string
}

func NewEntry(logger *Logger) *Entry {
	return &Entry{logger: logger}
}

func (entry *Entry) SetFields(data map[string]interface{}) *Entry {
	entry.Data = data
	return entry
}

func (entry *Entry) SetField(key string, value interface{}) *Entry {
	entry.Data[key] = value
	return entry
}

func (entry *Entry) DelField(key string) *Entry {
	if _, ok := entry.Data[key]; ok {
		delete(entry.Data, key)
	}
	return entry
}

func (entry *Entry) log(level Level, message string) {
	entry.Time = time.Now()
	entry.Level = level
	entry.Message = message

	if err := entry.logger.hooks.Fire(level, entry); err != nil {
		entry.logger.lock.Lock()
		fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
		entry.logger.lock.Unlock()
	}

	buffer := buffers.Get().(*bytes.Buffer)
	buffer.Truncate(0)
	defer buffers.Put(buffer)
	entry.buffer = buffer
	b, _ := entry.logger.formatter.Format(entry)
	entry.buffer = nil

	entry.logger.lock.Lock()
	entry.logger.writer.Write(b)
	entry.logger.lock.Unlock()
}

func (entry *Entry) Debug(args ...interface{}) {
	if entry.logger.GetLevel() >= Debug {
		entry.log(Debug, fmt.Sprint(args...))
	}
}

func (entry *Entry) Info(args ...interface{}) {
	if entry.logger.GetLevel() >= Infos {
		entry.log(Infos, fmt.Sprint(args...))
	}
}

func (entry *Entry) Warn(args ...interface{}) {
	if entry.logger.GetLevel() >= Warns {
		entry.log(Warns, fmt.Sprint(args...))
	}
}

func (entry *Entry) Error(args ...interface{}) {
	if entry.logger.GetLevel() >= Error {
		entry.log(Error, fmt.Sprint(args...))
	}
}

func (entry *Entry) Fatal(args ...interface{}) {
	if entry.logger.GetLevel() >= Fatal {
		entry.log(Fatal, fmt.Sprint(args...))
	}
	os.Exit(1)
}
